#ifndef HTTP_LIB_PUSH_H_
#define HTTP_LIB_PUSH_H_

#include "httplib.h"
#include "common.h"
#include "json.h"
#include <atomic>

using json = nlohmann::json;

class DrillPipeRigOverlapAreaInfo
{
public:
    // 构造函数，用于初始化DrillPipeRigOverlapArea对象
    DrillPipeRigOverlapAreaInfo(int frame_num, float angle, float angle_diff, double overlap_area)
        : frame_num_(frame_num), angle_(angle), angle_diff_(angle_diff), overlap_area_(overlap_area) {};

    ~DrillPipeRigOverlapAreaInfo() = default;

    int frame_num_;       // 帧数
    float angle_;         // 角度
    float angle_diff_;    // 角度变化量
    double overlap_area_; // 重叠面积
};

class DrillPipeAngleChangeInfo
{
public:
    // 构造函数，用于初始化DrillPipeRigOverlapArea对象
    DrillPipeAngleChangeInfo(int frame_num, float angle, cv::Point left_center_point, cv::Point right_center_point, float angle_diff_with_stable)
        : frame_num_(frame_num), angle_(angle), left_center_point_(left_center_point), right_center_point_(right_center_point), angle_diff_with_stable_(angle_diff_with_stable) {};

    ~DrillPipeAngleChangeInfo() = default;

    int frame_num_;                                    // 帧数
    float angle_;                                      // 角度
    cv::Point left_center_point_, right_center_point_; // 左侧和右侧中心点坐标
    float angle_diff_with_stable_;                     // 与稳定状态角度变化量
};

class DrillPipeStableAngleInfo
{
public:
    DrillPipeStableAngleInfo(cv::Point first_point, cv::Point second_point, float angle, float width)
        : first_point_(first_point), second_point_(second_point), angle_(angle), width_(width) {};

    ~DrillPipeStableAngleInfo() = default;

    cv::Point first_point_;
    cv::Point second_point_;
    float angle_;
    float width_;
};

class HttpLibPush
{

public:
    HttpLibPush(std::string server_ip_port,
                std::string path,
                int drill_pipe_angle_change_threshold_by_degree,
                int drill_pipe_angle_judge_continuous_time_by_second,
                int drill_pipe_angle_continuous_stable_judge_time_by_second,
                int max_que_size = 20)
        : http_cli(server_ip_port),
          server_ip_port_(server_ip_port),
          drill_pipe_angle_change_threshold_by_degree_(drill_pipe_angle_change_threshold_by_degree),
          drill_pipe_angle_judge_continuous_time_by_second_(drill_pipe_angle_judge_continuous_time_by_second),
          drill_pipe_angle_continuous_stable_judge_time_by_second_(drill_pipe_angle_continuous_stable_judge_time_by_second),
          path_(path), stop_flag_(false), max_que_size_(max_que_size)
    {
        bm_dev_request(&handle_, 0);
        workThread = std::make_shared<std::thread>(&HttpLibPush::dowork, this);
    }
    ~HttpLibPush()
    {
        stop_flag_ = true;
        workThread->join();
    };

    void start()
    {
        stop_flag_ = false;
        std::cout << "http push: " + server_ip_port_ + path_ + " start!" << std::endl;
    }

    void stop()
    {
        stop_flag_ = true;
        std::cout << "mqtt push: " + server_ip_port_ + path_ + +" stop!" << std::endl;
    }

    void add_channel(int channel_idx, std::string channel_idx_url);
    void push_img(int channel_idx, std::shared_ptr<FrameInfoDetectYolov8Seg> img_ptr);

private:
    bm_handle_t handle_;

    httplib::Client http_cli;
    std::shared_ptr<std::thread> workThread;
    std::string server_ip_port_;
    std::string path_;

    std::atomic<bool> stop_flag_;
    std::mutex stop_flag_mtx_;

    int max_que_size_;
    std::deque<std::pair<int, std::shared_ptr<FrameInfoDetectYolov8Seg>>> img_que_;
    std::mutex img_que_mtx_;

    std::vector<int> channel_idx_vec_;
    std::vector<std::string> channel_url_vec_;
    std::vector<std::shared_ptr<std::thread>> thread_vec_;

    /**
     * 存储钻杆的box以及钻杆的角度信息，
     * 钻机就用当前的就可以，因为钻机的位置基本不动，当然也可以取个平均值
     * 钻杆的角度信息，一张照片在识别的时候会出现多个钻杆，这些钻杆都要进行存储，以便进行后续的判断
     * */
    std::deque<YoloV8Box> drill_rig_boxes_;
    std::mutex drill_rig_boxes_mutex_;
    int drill_rig_boxes_max_size_ = 10;
    std::deque<std::vector<YoloV8Box>> drill_pipe_boxes_;
    std::deque<DrillPipeAngleChangeInfo> drill_pipe_angle_change_deque_;        // 存储钻杆角度变化信息，以便进行后续的判断
    std::deque<DrillPipeRigOverlapAreaInfo> drill_pipe_rig_overlap_area_deque_; // 存储钻杆和钻机的重叠面积信息，以便进行后续的判断,第一个是角度变化，第二个是重叠面积
    std::mutex drill_pipe_boxes_mutex_;
    std::vector<DrillPipeRigOverlapAreaInfo> drill_pipe_rig_overlap_area_tmp;
    int drill_pipe_boxes_max_size_ = 20;
    int drill_pipe_angle_change_threshold_by_degree_ = 3;       // 钻杆角度变化阈值，默认为5度,通过fps和skip_frame_num可以计算出计算钻杆角度的频率，从而得知钻杆角度
    int drill_pipe_angle_judge_continuous_time_by_second_ = 15; // 每多少秒进行一次钻杆角度的判断，通过fps和skip_frame_num可以计算出计算钻杆角度的频率，
    int frame_num_ = 0;                                         // 当前帧数，用于计算钻杆角度的频率
    int frame_fps_ = 25;                                        // 视频帧率，默认为25帧/秒
    int skip_frame_num_ = 10;                                   // 每跳过多少帧进行一次钻杆角度的计算，默认为1，即每帧都计算,通过fps和skip_frame_num可以计算出计算钻杆角度的频率，从而得知钻杆角度的更新频率
    int judge_drill_pipe_sleep_by_second_ = 20;                 // 在完成一次进退钻判断后，为了防止下一次进退钻判断过快，设置休眠时间，此处设置20秒
    int judge_sleep_frame_num_ = 0;                             // 在完成一次进退钻判断后，为了防止下一次进退钻判断过快，配置一个休眠帧数
    bool enable_judge_drill_pipe_ = true;                       // 是否启用进退钻判断，默认为true, 每次判断完钻杆角度后，会将此变量设置为false，到达休眠时间限定后，再置为true
    std::mutex frame_num_change_mutex_;                         // 帧数变化的互斥锁
    int drill_pipe_forwad_num_ = 0;                             // 进钻次数，默认为0
    int drill_pipe_backward_num_ = 0;                           // 退钻次数，默认为0

    /**
     * 对于顶板泄压打钻，因为钻孔基本都在巷道顶部，因此可简单通过获取处于图像上三分之一区域钻杆的角度信息来计算
     * 对于其他类型的钻场，因为打钻角度有水平或向下倾斜，因此需要根据别的信息进行计算，此处暂时不做处理
     */
    int drill_pipe_angle_continuous_stable_frame_num_ = 0;
    int drill_pipe_angle_continuous_stable_judge_time_by_second_ = 15; // 钻杆稳定状态判断持续时间，默认为15秒
    bool is_get_drill_pipe_stable_angle_ = false;                      // 是否获取到了钻杆稳定角度信息，默认为false，注意基本上等于钻孔开孔角度，即钻机打钻时夹持器往复的角度
    float drill_pipe_stable_angle_ = 0.0f;                             // 钻杆稳定角度，默认为0度
    bool is_drill_pipe_angle_change_detected = false;                  // 是否检测到钻杆角度变化，默认为false

    /**
     * 钻孔角度信息对应的两点，默认为(0,0)和(0,0),当发生钻杆角度变化时，检查钻杆与这个稳态的钻杆角度线的夹角角度变化，若从小（小于15度）变大（大于15度），则说明离开钻机，
     * 要确保每次的两边的变化都要超过3秒以上
     */
    int drill_pipe_angle_stable_points_tmp_max_size_ = 20;                          // 钻杆信息稳态存储的临时对象的大小，默认为20
    std::deque<DrillPipeStableAngleInfo> drill_pipe_stable_angle_points_tmp_deque_; // 钻杆信息稳态存储的临时对象，用于计算平均值
    cv::Point drill_pipe_stable_angle_center_point_;                                // 钻杆稳态角度的中心点，默认为(0,0)
    float drill_pipe_stable_mean_width_ = 0.0f;                                     // 钻杆稳态角度的平均宽度，默认为0

    /**
     * 判断钻杆的前后方向，0为无状态，1为向前，-1为向后
     * 参数就是用上面那些
     * */
    int judge_drill_pipe_forward_backward_direction();

    std::string base64_encode_bmcv(bm_handle_t handle_, unsigned char *jpegData, size_t nBytes);

    void dowork();

    bool get_stop_flag()
    {
        std::lock_guard<std::mutex> lock(stop_flag_mtx_);
        return stop_flag_;
    }
};

void HttpLibPush::add_channel(int channel_idx, std::string channel_idx_url)
{
    channel_idx_vec_.push_back(channel_idx);
    channel_url_vec_.push_back(channel_idx_url);
}

void HttpLibPush::push_img(int channel_idx, std::shared_ptr<FrameInfoDetectYolov8Seg> img_ptr)
{
    if (channel_idx_vec_.empty())
    {
        std::cout << "channel_idx is empty" << std::endl;
        return;
    }
    if (channel_idx_vec_.end() == std::find(channel_idx_vec_.begin(), channel_idx_vec_.end(), channel_idx))
    {
        std::cout << "channel_idx not found" << std::endl;
        return;
    }
    {
        std::lock_guard<std::mutex> lock(img_que_mtx_);
        if (img_que_.size() >= max_que_size_)
        {
            std::cout << "http_lib_push img_que is full" << std::endl;
            img_que_.pop_front();
        }
        img_que_.emplace_back(std::make_pair(channel_idx, img_ptr));
    }
}

std::string HttpLibPush::base64_encode_bmcv(bm_handle_t handle_, unsigned char *jpegData,
                                            size_t nBytes)
{
    // for bmcv
    unsigned long origin_len[2] = {nBytes, 0};
    unsigned long encode_len[2] = {(origin_len[0] + 2) / 3 * 4, 0};
    std::string res(encode_len[0], '\0');
    bmcv_base64_enc(handle_, bm_mem_from_system(jpegData),
                    bm_mem_from_system(const_cast<char *>(res.c_str())),
                    origin_len);
    return res;
}

void HttpLibPush::dowork()
{
    std::cout << "Http lib push doworking" << std::endl;

    while (true)
    {
        if (get_stop_flag())
        {
            break;
        }

        std::lock_guard<std::mutex> lock(img_que_mtx_);

        if (!img_que_.empty())
        {
            std::shared_ptr<FrameInfoDetectYolov8Seg> img_ptr;
            int channel_idx;
            {
                channel_idx = img_que_.front().first;
                img_ptr = img_que_.front().second;
                img_que_.pop_front();
            }
            /**
             * 钻杆进退钻的判断，0是没有判断，1是进钻，-1是退钻
             */
            int drill_pipe_forward_backward_direction = 0;
            {
                std::unique_lock<std::mutex> frame_num_lock(frame_num_change_mutex_);
                frame_num_ += (skip_frame_num_ + 1);
                frame_num_lock.unlock();
                /**
                 * 此处可以添加一些额外的处理，比如钻杆进钻和退钻的识别
                 */
                if (img_ptr->is_mask)
                {
                    std::vector<YoloV8Box> boxes = img_ptr->boxs_vec;
                    if (boxes.size() > 0)
                    {
                        std::vector<YoloV8Box> drill_pipe_boxes_tmp;
                        for (int i = 0; i < boxes.size(); i++)
                        {
                            int width = boxes[i].x2 - boxes[i].x1;
                            int height = boxes[i].y2 - boxes[i].y1;
                            if (boxes[i].class_id == 3) // drill-pipe
                            {
                                // 此处注意，一个照片会识别多个钻杆，所以用一个队列来保存最新的drill-pipe信息
                                drill_pipe_boxes_tmp.emplace_back(boxes[i]);
                            }
                            if (boxes[i].class_id == 4) // drill-rig 将获得的钻机放入队列中，以便后续使用
                            {
                                std::unique_lock<std::mutex> drill_rig_lock(drill_rig_boxes_mutex_);
                                if (drill_rig_boxes_.size() >= drill_rig_boxes_max_size_)
                                {
                                    drill_rig_boxes_.pop_front();
                                }
                                drill_rig_boxes_.emplace_back(boxes[i]);
                                drill_rig_lock.unlock();
                            }
                        }
                        // 如果有drill-pipe信息，则放入队列中
                        if (drill_pipe_boxes_tmp.size() > 0)
                        {
                            std::unique_lock<std::mutex> drill_pipe_boxes_lock(drill_pipe_boxes_mutex_);
                            if (drill_pipe_boxes_.size() >= drill_pipe_boxes_max_size_)
                            {
                                drill_pipe_boxes_.pop_front();
                            }
                            drill_pipe_boxes_.emplace_back(drill_pipe_boxes_tmp);
                            drill_pipe_boxes_lock.unlock();

                            // 本次识别获取了钻杆信息，则进行钻杆的进退钻计算
                            if (enable_judge_drill_pipe_)
                            {
                                drill_pipe_forward_backward_direction = judge_drill_pipe_forward_backward_direction();
                                switch (drill_pipe_forward_backward_direction)
                                {
                                case 1:
                                    std::cout << "drill pipe is forward" << std::endl;
                                    break;
                                case -1:
                                    std::cout << "drill pipe is backward" << std::endl;
                                    break;
                                default:
                                    break;
                                }
                            }
                        }
                    }
                }
                if (!enable_judge_drill_pipe_)
                {
                    frame_num_lock.lock();
                    judge_sleep_frame_num_ += (skip_frame_num_ + 1);
                    if ((judge_sleep_frame_num_ / frame_fps_) > judge_drill_pipe_sleep_by_second_)
                    {
                        enable_judge_drill_pipe_ = true;
                        judge_sleep_frame_num_ = 0;
                    }
                    frame_num_lock.unlock();
                }
            }

            {
                nlohmann::json json_data_obj;
                json_data_obj["channel_idx"] = channel_idx;
                json_data_obj["channel_url"] = channel_url_vec_[channel_idx];
                json_data_obj["is_mask"] = img_ptr.get()->is_mask;
                json_data_obj["width"] = img_ptr.get()->image_ptr->width;
                json_data_obj["height"] = img_ptr.get()->image_ptr->height;

                // 如果钻杆进退钻判断为 正负1，-1为退钻 1为进钻 ，非零的时候记得推送照片，否则不推送
                if (drill_pipe_forward_backward_direction != 0)
                {
                    // jpeg 的 base64 编码耗资源，耗带宽，可暂时不用
                    std::string img_base64_data;
                    void *jpeg_data = NULL;
                    size_t out_size = 0;
                    bmcv_image_jpeg_enc(handle_, 1, img_ptr->image_ptr.get(),
                                        &jpeg_data, &out_size);
                    img_base64_data = base64_encode_bmcv(handle_,
                                                         (unsigned char *)jpeg_data, out_size);
                    free(jpeg_data);
                    json_data_obj["img_base64_data"] = img_base64_data;
                }
                json_data_obj["drill_pipe_forward_backward_direction"] = drill_pipe_forward_backward_direction;

#if 1
                // yolov8seg 检测结果
                std::vector<YoloV8Box> boxs_vec = img_ptr->boxs_vec;
                json box_json = json::array();
                for (auto &box : boxs_vec)
                {
                    json hullPoints_json = json::array();
                    for (auto &point : box.hullPoints)
                    {
                        hullPoints_json.push_back(
                            json({{"x", point.x + box.x1}, {"y", point.y + box.y1}})); // 加上左上角坐标，移动到图片中的位置
                    }
                    box_json.push_back(
                        json(json::object({{"x1", box.x1},
                                           {"y1", box.y1},
                                           {"x2", box.x2},
                                           {"y2", box.y2},
                                           {"score", box.score},
                                           {"class_id", box.class_id},
                                           {"class_name", box.class_name},
                                           //    {"mask", box.mask}, // 暂时不传mask数据，意义不大
                                           {"angle", box.angle},
                                           {"length", box.length},
                                           {"hullPoints", hullPoints_json}})));
                }
                json_data_obj["boxs"] = box_json;
#endif

                std::string json_data = json_data_obj.dump();

                if (auto res = http_cli.Post(path_.c_str(), json_data, "application/json"))
                {
                    // std::cout << res->status << std::endl;
                    // std::cout << res->get_header_value("Content-Type") << std::endl;
                    // std::cout << res->body << std::endl;
                }
                else
                {
                    std::cout << res.error() << std::endl;
                }
            }
        }
    }
    std::cout << "Http lib push dowork stop" << std::endl;
}

// 计算两个矩形的交集矩形，并返回其面积
double calculateOverlapArea(const cv::Rect &rect1, const cv::Rect &rect2)
{
    // 计算交集的坐标
    int x1 = std::max(rect1.x, rect2.x);
    int y1 = std::max(rect1.y, rect2.y);
    int x2 = std::min(rect1.x + rect1.width, rect2.x + rect2.width);
    int y2 = std::min(rect1.y + rect1.height, rect2.y + rect2.height);

    // 检查是否有交集
    if (x1 >= x2 || y1 >= y2)
    {
        return 0.0; // 没有交集
    }

    // 计算交集矩形的宽度和高度
    int width = x2 - x1;
    int height = y2 - y1;

    // 计算交集矩形的面积
    double overlapArea = static_cast<double>(width) * static_cast<double>(height);
    return overlapArea;
}

double calculateDistanceBetweenRectCenters(const cv::Rect &rect1, const cv::Rect &rect2)
{
    // 计算第一个矩形的中心点坐标
    double centerX1 = rect1.x + rect1.width / 2.0;
    double centerY1 = rect1.y + rect1.height / 2.0;

    // 计算第二个矩形的中心点坐标
    double centerX2 = rect2.x + rect2.width / 2.0;
    double centerY2 = rect2.y + rect2.height / 2.0;

    // 计算两个中心点之间的距离
    double distance = std::sqrt(std::pow(centerX2 - centerX1, 2) + std::pow(centerY2 - centerY1, 2));

    return distance;
}

double calculateIOU(const cv::Rect &rect1, const cv::Rect &rect2)
{
    // 计算交集矩形
    cv::Rect intersection = rect1 & rect2;

    // 如果交集矩形不存在（宽度或高度小于0），则IOU为0
    if (intersection.area() <= 0)
    {
        return 0.0;
    }

    // 计算两个矩形的面积之和
    double area1 = rect1.area();
    double area2 = rect2.area();

    // 计算IOU比率
    double iou = static_cast<double>(intersection.area()) / (area1 + area2 - intersection.area());
    // 但由于intersection.area()在并集中只被考虑一次，所以上式可以简化为：
    // double iou = static_cast<double>(intersection.area()) / (area1 + area2 - (只考虑一次的交集面积，这里已经考虑过了，所以直接写并集面积的计算公式是错误的，应直接使用上面的公式结果，即已简化的形式))
    // 正确的简化形式为：
    // double iou = static_cast<double>(intersection.area()) / (正确的并集面积计算公式在这里已经通过area1 + area2 - intersection.area()得到了，但注意这是为了解释，实际上直接写上面的公式即可)
    // 为了避免混淆，这里不再写出错误的简化形式，直接使用正确的公式结果：
    return iou; // 这里返回的已经是简化并修正后的IOU计算结果了
}

/**
 * @brief 计算点到直线的距离
 *
 * 根据给定的点坐标(px, py)和直线参数(cx, cy, theta)，计算点到直线的距离。
 *
 * @param px 给定点的x坐标
 * @param py 给定点的y坐标
 * @param cx 直线上一点的x坐标
 * @param cy 直线上一点的y坐标
 * @param theta 直线的倾斜角度（单位：度）
 *
 * @return 返回点到直线的距离
 */
double calculateDistanceToLine(double px, double py, double cx, double cy, double theta)
{
    // 将角度转换为弧度
    double radians = theta * M_PI / 180.0;

    // 计算法向量的分量
    double A = -sin(radians);

    double B = cos(radians);

    // 计算C
    double C = cx * sin(radians) - cy * cos(radians);

    // 应用点到直线的距离公式
    double distance = std::abs(A * px + B * py + C);

    return distance;
}

int HttpLibPush::judge_drill_pipe_forward_backward_direction()
{
    // 首先计算钻机box的平局值，x1 y1 x2 y2 的平均值, 若没有探测到钻机，则不进行判断，直接返回0
    float drill_rig_boxes_mean_value_x1, drill_rig_boxes_mean_value_y1, drill_rig_boxes_mean_value_x2, drill_rig_boxes_mean_value_y2;
    {
        bool drill_rig_boxes_is_empty = true;
        std::unique_lock<std::mutex> drill_rig_lock(drill_rig_boxes_mutex_);
        if (drill_rig_boxes_.size() > 0)
        {
            drill_rig_boxes_is_empty = false;
            std::vector<float> x1_vec_tmp;
            std::vector<float> y1_vec_tmp;
            std::vector<float> x2_vec_tmp;
            std::vector<float> y2_vec_tmp;
            int drill_rig_boxes_size = drill_rig_boxes_.size();
            for (int i = 0; i < drill_rig_boxes_size; i++)
            {
                x1_vec_tmp.emplace_back(drill_rig_boxes_[i].x1);
                y1_vec_tmp.emplace_back(drill_rig_boxes_[i].y1);
                x2_vec_tmp.emplace_back(drill_rig_boxes_[i].x2);
                y2_vec_tmp.emplace_back(drill_rig_boxes_[i].y2);
            }
            drill_rig_boxes_mean_value_x1 = accumulate(x1_vec_tmp.begin(), x1_vec_tmp.end(), 0.0) / drill_rig_boxes_size;
            drill_rig_boxes_mean_value_y1 = accumulate(y1_vec_tmp.begin(), y1_vec_tmp.end(), 0.0) / drill_rig_boxes_size;
            drill_rig_boxes_mean_value_x2 = accumulate(x2_vec_tmp.begin(), x2_vec_tmp.end(), 0.0) / drill_rig_boxes_size;
            drill_rig_boxes_mean_value_y2 = accumulate(y2_vec_tmp.begin(), y2_vec_tmp.end(), 0.0) / drill_rig_boxes_size;
        }
        drill_rig_lock.unlock();
#if 0
    std::cout << "drill_rig_boxes_mean_value x1: " << drill_rig_boxes_mean_value_x1 << " y1: " << drill_rig_boxes_mean_value_y1
              << " x2: " << drill_rig_boxes_mean_value_x2 << " y2: " << drill_rig_boxes_mean_value_y2 << std::endl;
#endif
        // 判断drill_rig_boxes是否为空，如果为空则直接返回0
        if (drill_rig_boxes_is_empty)
        {
            return 0;
        }
    }

    // 之后判断钻杆是否发生变化，在钻杆角度发生变化后只需要关注钻杆角度与钻杆稳态角度相差大于drill_pipe_angle_change_threshold_by_degree_ 的钻杆。
    std::unique_lock<std::mutex> drill_pipe_boxes_lock(drill_pipe_boxes_mutex_);
    std::unique_lock<std::mutex> frame_num_lock(frame_num_change_mutex_);

    if (drill_pipe_boxes_.size() > 2)
    {
        std::vector<YoloV8Box> current_drill_pipe_boxes = drill_pipe_boxes_.back();
        std::vector<YoloV8Box> previous_drill_pipe_boxes = drill_pipe_boxes_.at(drill_pipe_boxes_.size() - 2);

        /**
         * 计算当前帧和上一帧钻杆的角度差，此处注意，因为一帧可能有多个钻杆，所以此处需要计算所有钻杆的角度差，
         * 并从中选取角度差最大的，且能够超过钻杆角度差阀值，则说明钻杆角度发生了变化，需要记录钻杆角度差所用的钻杆的序号
         * 以便进行下一步的钻杆与钻机box重合面积的计算
         * ----------------------------------
         * 此处算法在这个地方有问题，因为当退钻的时候，按照当下的钻杆选择逻辑，每次发生退钻的时候，退钻判断后下一帧就会把钻机上的钻杆作为
         * 钻杆角度变化最大的钻杆，但是实际上，钻机上的钻杆角度变化很小，并不能代表钻杆的角度发生了变化，此时由于钻机上的钻杆与钻杆稳定角度一致，此时判定为进钻
         * 下一帧又开始进行判断退钻，所以退钻不可能判断成功，相反，当退钻工作完成后，进钻判断能够判断成功，所以此处判断逻辑有问题。
         * -------------------------------------
         * 此处我们更换一个思路，当钻杆角度变化超过阀值时，此时我们把这个钻杆作为关注钻杆，后续判断的钻杆为与关注钻杆距离最近的钻杆，即认定为这个钻杆为关注钻杆，
         * 后面对他进行判断。关注钻杆的位置随着进行更新。当钻杆数量等于1时，且钻杆角度与钻杆稳定角度一致时，此时将钻杆角度变化的标识位置为false。
         * 利用两个变量进行控制
         * is_drill_pipe_angle_change_detected 用于判断钻杆角度是否发生了变化
         *
         */
        float max_drill_pipe_angle_diff = 0.0;
        int current_drill_pipe_box_num = 0;
        int previous_drill_pipe_box_num = 0;
        /**
         * 如果没有探测到钻杆角度变化，则遍历当前帧和上一帧的钻杆，计算所有钻杆的角度差,之后选取角度差最大的钻杆，如果这个最大值超过了drill_pipe_angle_change_threshold_by_degree_，
         * 则认为钻杆角度发生了变化
         * */
        if (is_drill_pipe_angle_change_detected == false)
        {
            for (int i = 0; i < current_drill_pipe_boxes.size(); i++)
            {
                for (int j = 0; j < previous_drill_pipe_boxes.size(); j++)
                {
                    float current_angle = current_drill_pipe_boxes[i].angle;
                    float previous_angle = previous_drill_pipe_boxes[j].angle;
                    float angle_diff = abs(current_angle - previous_angle);
                    if (angle_diff > max_drill_pipe_angle_diff)
                    {
                        max_drill_pipe_angle_diff = angle_diff;
                        current_drill_pipe_box_num = i;
                        previous_drill_pipe_box_num = j;
                    }
                }
            }
            if (max_drill_pipe_angle_diff > drill_pipe_angle_change_threshold_by_degree_)
            {
                drill_pipe_angle_continuous_stable_frame_num_ = 0; // 重置稳态帧数计数
                is_drill_pipe_angle_change_detected = true;
            }
            /***
             * 若钻杆角度变化不大，则认为是稳态，此时要进行计数，若连续超过设定时间（drill_pipe_angle_continuous_stable_judge_time_by_second_），则认为钻杆角度
             * 进入（或重新计入）稳态，此时重新计算稳态角度，并以此作为基准进行后续判断
             * 注意此方法用于新建稳态数据和刷新稳态值，用于当钻机移动位置后进行修正判断
             */
            else
            {
                /**
                 * 钻杆角度变化不大，则认为是稳态，此时要进行计数，若连续超过30秒都在这个角度，且钻杆与钻机box重合度比较高，此处设置3000像素以上
                 * 则将这个角度配置为稳态角度，脱离这个角度则为退钻，进入这个角度则为进钻
                 * */
                drill_pipe_angle_continuous_stable_frame_num_ += (skip_frame_num_ + 1); // 稳态帧数累加 加上跳帧数
                std::cout << "drill_pipe_angle_continuous_stable_frame_num_ is : " << drill_pipe_angle_continuous_stable_frame_num_ << std::endl;
                if (drill_pipe_stable_angle_points_tmp_deque_.size() > drill_pipe_angle_stable_points_tmp_max_size_)
                {
                    drill_pipe_stable_angle_points_tmp_deque_.pop_front();
                }
                drill_pipe_stable_angle_points_tmp_deque_.emplace_back(DrillPipeStableAngleInfo(current_drill_pipe_boxes[current_drill_pipe_box_num].left_center_point,
                                                                                                current_drill_pipe_boxes[current_drill_pipe_box_num].right_center_point,
                                                                                                current_drill_pipe_boxes[current_drill_pipe_box_num].angle,
                                                                                                current_drill_pipe_boxes[current_drill_pipe_box_num].width));
                if (drill_pipe_angle_continuous_stable_frame_num_ > (drill_pipe_angle_continuous_stable_judge_time_by_second_ * frame_fps_))
                {
                    // 计算钻杆稳态位置，取中心点平均值作为中点，取平均角度作为稳态角度

                    cv::Point first_point = cv::Point(0, 0);
                    cv::Point second_point = cv::Point(0, 0);
                    float angle = 0.0;
                    float width = 0.0;
                    for (int i = 0; i < drill_pipe_stable_angle_points_tmp_deque_.size(); i++)
                    {
                        first_point.x += (drill_pipe_stable_angle_points_tmp_deque_[i].first_point_.x);
                        first_point.y += (drill_pipe_stable_angle_points_tmp_deque_[i].first_point_.y);
                        second_point.x += (drill_pipe_stable_angle_points_tmp_deque_[i].second_point_.x);
                        second_point.y += (drill_pipe_stable_angle_points_tmp_deque_[i].second_point_.y);
                        angle += (drill_pipe_stable_angle_points_tmp_deque_[i].angle_);
                        width += (drill_pipe_stable_angle_points_tmp_deque_[i].width_);
                    }
                    first_point.x /= drill_pipe_stable_angle_points_tmp_deque_.size();
                    first_point.y /= drill_pipe_stable_angle_points_tmp_deque_.size();
                    second_point.x /= drill_pipe_stable_angle_points_tmp_deque_.size();
                    second_point.y /= drill_pipe_stable_angle_points_tmp_deque_.size();
                    angle /= drill_pipe_stable_angle_points_tmp_deque_.size();
                    width /= drill_pipe_stable_angle_points_tmp_deque_.size();

#if 1
                    std::cout << "drill_pipe_stable_angle_center_point_ is :( " << drill_pipe_stable_angle_center_point_.x
                              << ", " << drill_pipe_stable_angle_center_point_.y << " ) anglle is: " << angle
                              << " width is : " << width << std::endl;
#endif

                    is_get_drill_pipe_stable_angle_ = true;
                    drill_pipe_stable_angle_center_point_ = (first_point + second_point) / 2;
                    drill_pipe_stable_angle_ = angle;
                    drill_pipe_stable_mean_width_ = width;
                }
            }
        }

        // 如果钻杆角度变化已经被检测到，则只需要关注钻杆角度差超过drill_pipe_angle_change_threshold_by_degree_的钻杆
        if (is_drill_pipe_angle_change_detected == true)
        {
            double angle_diff_max_with_stable_angle = 0.0; // 只关注与关注钻杆角度变化最大的钻杆
            for (int i = 0; i < current_drill_pipe_boxes.size(); i++)
            {
                double angel_diff_value_with_stable_angle = abs(current_drill_pipe_boxes[i].angle - drill_pipe_stable_angle_);
                if (angel_diff_value_with_stable_angle > angle_diff_max_with_stable_angle)
                {
                    current_drill_pipe_box_num = i;
                    angle_diff_max_with_stable_angle = angel_diff_value_with_stable_angle;
                }
            }
            std::cout << " angle_diff_max_with_stable_angle : " << angle_diff_max_with_stable_angle << std::endl;

            if (angle_diff_max_with_stable_angle > drill_pipe_angle_change_threshold_by_degree_)
            {
                drill_pipe_angle_change_deque_.emplace_back(frame_num_,
                                                            current_drill_pipe_boxes[current_drill_pipe_box_num].angle,
                                                            current_drill_pipe_boxes[current_drill_pipe_box_num].left_center_point,
                                                            current_drill_pipe_boxes[current_drill_pipe_box_num].right_center_point,
                                                            max_drill_pipe_angle_diff);
            }

            DrillPipeAngleChangeInfo previous_drill_pipe_angle_change_info = drill_pipe_angle_change_deque_.front();
            int frame_num_diff = frame_num_ - previous_drill_pipe_angle_change_info.frame_num_;
            double elapsed_time_by_second = frame_num_diff * 1.0 / frame_fps_;
            if (elapsed_time_by_second > 2) // 等待两秒，如果钻杆角度变化已经超过2s，则认为钻杆角度变化结束，此时进行钻杆进退钻的判断
            {
                is_drill_pipe_angle_change_detected = false;
                // 进行进退钻的判断，此处判断钻杆中心点与钻杆固定角度所在线的垂直距离，如果距离逐渐变小，则说明进钻，如果逐渐变大，则说明退钻
                std::cout << "drill_pipe_angle_change_deque_ size is : " << drill_pipe_angle_change_deque_.size() << std::endl;
                // for (int i = 0; i < drill_pipe_angle_change_deque_.size(); i++)
                // {
                //     std::cout << "drill pipe rig overlap area tmp frame_num_ is : " << drill_pipe_angle_change_deque_.at(i).frame_num_ << std::endl;
                // }
                if (drill_pipe_angle_change_deque_.size() > 0)
                {
                    // 超过两次识别到才能计算趋势
                    // 经过观察发现，进钻和退钻过程相比，进钻过程中钻杆角度与稳定角度的差值偏小的时间更长，因此可以通过对比钻杆角度差超过
                    // drill_pipe_angle_change_threshold_by_degree_ * 2 的钻杆角度差，来判断趋势,如果更多时间处于角度更小的情况，则说明是进钻，反正是进钻
                    // 进钻的判断比率是超过0.6 的时间在 drill_pipe_angle_change_threshold_by_degree_ * 2 的钻杆角度差的时间占比
                    // 其他情况认为是退钻
                    int drill_forward_frame_num = 0;
                    int drill_backward_frame_num = 0;
                    float latest_drill_pipe_angle_diff = drill_pipe_angle_change_deque_.back().angle_ - drill_pipe_stable_angle_;
                    for (int i = 0; i < drill_pipe_angle_change_deque_.size(); i++)
                    {
                        DrillPipeAngleChangeInfo drill_pipe_angle_change_info = drill_pipe_angle_change_deque_[i];
                        double left_center_point_distance_with_stable_pipe_line = calculateDistanceToLine(drill_pipe_angle_change_info.left_center_point_.x,
                                                                                                          drill_pipe_angle_change_info.left_center_point_.y,
                                                                                                          drill_pipe_stable_angle_center_point_.x,
                                                                                                          drill_pipe_stable_angle_center_point_.y,
                                                                                                          drill_pipe_stable_angle_);
                        double right_center_point_distance_with_stable_pipe_line = calculateDistanceToLine(drill_pipe_angle_change_info.right_center_point_.x,
                                                                                                           drill_pipe_angle_change_info.right_center_point_.y,
                                                                                                           drill_pipe_stable_angle_center_point_.x,
                                                                                                           drill_pipe_stable_angle_center_point_.y,
                                                                                                           drill_pipe_stable_angle_);
                        double left_right_distance_diff = abs(left_center_point_distance_with_stable_pipe_line - right_center_point_distance_with_stable_pipe_line);
                        double angle_diff = abs(drill_pipe_angle_change_info.angle_ - drill_pipe_stable_angle_);
                        std::cout
                            //   << " left_distanceis : " << left_center_point_distance_with_stable_pipe_line
                            //   << " right_distance is : " << right_center_point_distance_with_stable_pipe_line
                            //   << " diff is : " << left_right_distance_diff
                            //   << " angle is : " << drill_pipe_angle_change_info.angle_
                            << " frame_num is : " << drill_pipe_angle_change_info.frame_num_
                            << " angle_diff is : " << drill_pipe_angle_change_info.angle_ - drill_pipe_stable_angle_
                            //   << " bigger one is : " << (left_center_point_distance_with_stable_pipe_line > right_center_point_distance_with_stable_pipe_line ? left_center_point_distance_with_stable_pipe_line : right_center_point_distance_with_stable_pipe_line)
                            << std::endl;
                        if (angle_diff >= drill_pipe_angle_change_threshold_by_degree_ * 2)
                        {
                            drill_backward_frame_num++;
                        }
                        else
                        {
                            drill_forward_frame_num++;
                        }
                    }
                    is_drill_pipe_angle_change_detected = false;
                    drill_pipe_angle_change_deque_.clear(); // 清空一下用于下一步的检测
                    if (drill_forward_frame_num > (drill_backward_frame_num + drill_forward_frame_num) * 0.6)
                    {
                        // 此处增加一个修正，如果最后一个钻杆的角度变化超过drill_pipe_angle_change_threshold_by_degree_*2，则认为是钻杆角度变化过大，认为是退钻
                        if (latest_drill_pipe_angle_diff >= drill_pipe_angle_change_threshold_by_degree_ * 2)
                        {
                            return -1; // 退钻
                        }
                        return 1; // 进钻
                    }
                    else
                    {
                        return -1; // 退钻
                    }
                }
                else
                {
                    is_drill_pipe_angle_change_detected = false;
                    drill_pipe_angle_change_deque_.clear(); // 清空一下用于下一步的检测
                }
            }
        }
    }
    frame_num_lock.unlock();
    drill_pipe_boxes_lock.unlock();
    return 0;
}

#endif // HTTP_LIB_PUSH_H_